/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hamming;

//import Gui.Gui;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 
 * @author Neo83
 *
 */
public class HammingDecoder {

    private int n;
    private int k;
    private byte H[][];
    //sindrome
    private byte sindrome[];
    private byte blocco[];
    private int posizioniBitDati[];
    private int erroriRilevati;
    int avanzamentoProcesso=0;
   
    public int getErroriRilevati() {
        return erroriRilevati;
    }

    public HammingDecoder(int n, int k) {
        erroriRilevati = 0;
        this.n = n;
        this.k = k;
        posizioniBitDati = new int[k];
        sindrome = new byte[n - k];
        blocco = new byte[n];
        creaMatriceH(n, k);
    }

    public String decode(String sequenzaCodificata) {
        StringBuilder seqDecodificata = new StringBuilder();
        int length = sequenzaCodificata.length();
        int inizio = 0;
        int fine = n;

        while (fine <= length) {
            String s = sequenzaCodificata.substring(inizio, fine);
            for (int i = 0; i < s.length(); i++) {
                switch (s.charAt(i)) {
                    case '0':
                        blocco[i] = 0;
                        break;
                    case '1':
                        blocco[i] = 1;
                        break;
                }
            }
            inizio = fine;
            fine += n;
            moltiplica(H, blocco);

            int index = estraiValore();
            if (index != -1) {
                flipBit(index);
                erroriRilevati++;
            }
            seqDecodificata.append(estraiBitDati());
        /*    if(Gui.flagBar==true){
             avanzamentoProcesso+=n;//per la progressBar della gui
            Gui.jProgressBarDH.setValue(Math.round(avanzamentoProcesso/length)*100);
            }*/
        }
        return seqDecodificata.toString();

    }

    private void creaMatriceH(int n, int k) {
        H = new byte[n - k][n];
        int passo = 1;
        byte b = 0;

        for (int i = 0; i < (n - k); i++) {
            int cont = 0;
            for (int j = 0; j <= n; j++) {
                if (j != 0) {
                    H[i][j - 1] = b;
                }

                cont++;
                if (cont == passo) {
                    cont = 0;
                    if (b == 0) {
                        b = 1;
                    } else {
                        b = 0;
                    }
                }
            }
            passo *= 2;
        }

        passo = 1;
        int i = 1;
        for (int j = 0; j < k; j++) {

            while (i == passo) {
                i++;
                passo = passo * 2;
            }
            posizioniBitDati[j] = i - 1;
            i++;
        }
    }

    private void moltiplica(byte[][] H, byte[] blocco) {
        byte prod;
        byte sum;

        for (int i = 0; i < (n - k); i++) {
            sum = 0;
            for (int j = 0; j < n; j++) {
                prod = (byte) (H[i][j] * blocco[j]);
                sum = (byte) (sum ^ prod);
            }
            sindrome[i] = sum;
        }

    }

    private String stampaSindrome() {
        char[] s = new char[sindrome.length];
        for (int i = 0; i < sindrome.length; i++) {
            if (sindrome[i] == 0) {
                s[i] = '0';
            } else {
                s[i] = '1';
            }
        }
        return new String(s, 0, sindrome.length);
    }

    private int estraiValore() {
        int valore = 0;
        for (int i = 0; i < sindrome.length; i++) {
            if (sindrome[i] != 0) {
                valore += Math.pow(2, i);
            }

        }
        return valore-1;
    }

    private void flipBit(int index) {
        byte b = blocco[index];
        if (b == 0) {
            blocco[index] = 1;
        } else {
            blocco[index] = 0;
        }

    }

    private String estraiBitDati() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < k; i++) {
            int posizione = posizioniBitDati[i];
            sb.append(blocco[posizione]);
        }
        return sb.toString();

    }

    public String decodificaDettagliata(String sequenzaCodificata) {
        PrintWriter writer = null;
        StringBuilder seqDecodificata = new StringBuilder();
        try {
            File file = new File("dettagli.txt");
            writer = new PrintWriter(new BufferedWriter(new FileWriter(file, true)));

            int length = sequenzaCodificata.length();
            int inizio = 0;
            int fine = n;

            while (fine <= length) {
                String s = sequenzaCodificata.substring(inizio, fine);
                for (int i = 0; i < s.length(); i++) {
                    switch (s.charAt(i)) {
                        case '0':
                            blocco[i] = 0;
                            break;
                        case '1':
                            blocco[i] = 1;
                            break;
                    }
                }
                inizio = fine;
                fine += n;
                moltiplica(H, blocco);


                int index = estraiValore();
                String sin = stampaSindrome();
                writer.println(s + " "+stampaSindrome()+" "+index);
                if (index != -1) {
                    flipBit(index);
                    erroriRilevati++;
                }
                seqDecodificata.append(estraiBitDati());
            }

            writer.close();

        } catch (IOException ex) {
            Logger.getLogger(HammingDecoder.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            writer.close();
        }
        return seqDecodificata.toString();

    }
}
